package edu.gmu.server;

import edu.gmu.messages.ResourceRequest;
import edu.gmu.messages.ResourceResponse;
import edu.gmu.middleware.RemoteStub;
import edu.gmu.middleware.Message;
import edu.gmu.middleware.Sink;
import edu.gmu.middleware.SinkFactory;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

public class TCPEchoServer
{
    private ServerSocket serverSocket = null;
    private int port = 0;
    private int id   = 0;
    private String name = null;
    
    private volatile boolean stopped = false;
    
    private List clients = new ArrayList();
    
    
    /**
     * Creates a new TCPEchoServer with specified params.
     */
    public TCPEchoServer( int port, int id, String name )
    {
        this.port = port;
        this.id   = id;
        this.name = name;
    }
    
    /**
     * Starts the server listening on specified port - blocks caller's thread
     * until finished.
     * @param int port
     * @throws java.io.IOException
     */
    public void listenRequests( ) throws IOException
    {
        this.serverSocket = new ServerSocket(this.port);
        
        this.stopped = false;
        while( stopped == false )
        {
            // Blocks until client connects
            final Socket clientSocket = this.serverSocket.accept();
            
            System.out.println("TCPEchoServer - Client connected.");
            /*
             * Handles in separate thread
             */
            Runnable task = new Runnable()
            {
                public void run()
                {
                    handleClient(clientSocket);
                }
            };
            Thread clientThread = new Thread( task );
            clientThread.setName( "HandlerThread-" );
            clientThread.start();
        }
    }
    
    /**
     * Handles a new client connection.
     * @param Socket clientSocket
     */
    public void handleClient( Socket clientSocket )
    {
        //--------------------------------------------------------------------//
        // Executed in handlerThread
        //--------------------------------------------------------------------//
        try
        {
            
            Sink sink = SinkFactory.makeTCPSink(clientSocket );
            RemoteStub dispatcher = new RemoteStub( sink, id, name );
            dispatcher.register(100, ResourceRequest.class);
            // This is adding to shared list from this thread - need to protect
            this.clients.add(dispatcher);
            
            /*
             * Read from clientsocket continusouyly until error or disconnect 
             */
            //boolean disconnect = false;
            //while( disconnect == false )
            //{
                Message message = dispatcher.receive();
                ResourceRequest request = (ResourceRequest)message;
                
                ResourceResponse response = new ResourceResponse();
                //response.count   = request.count++;
                //response.missive = request.missive;

                dispatcher.send(101, response);
                
                dispatcher.stop();
            //}
        }
        catch(IOException ioe)
        {
            ioe.printStackTrace();
        }
        //--------------------------------------------------------------------//
    }
    
    /**
     * Stops the server from listening
     * @throws java.io.IOException
     */
    public void stop()
    {
        this.stopped = true;
        // Stop server - so that we accept no more clients
        if( this.serverSocket != null )
        {
            try
            {
                this.serverSocket.close();
            }
            catch(IOException ioe)
            {
                // ignore
            }
        }
        
        // Stop clients
        for (int i = 0; i < clients.size(); i++)
        {
            RemoteStub stub = (RemoteStub)clients.get(i);
            stub.stop();
        }
    }
 
    
    /**
     * Solely to be used for debugging.
     * @param inBuffer
     * @param length
     */
    private static void print( ByteBuffer inBuffer, int length )
    {
        byte[] in = inBuffer.array();
        System.out.print( "position="+inBuffer.position() + " limit="+inBuffer.limit() );
        for (int j = 0; j < length; j++)
        {
            String s = Integer.toHexString( in[j]&0xff );
            if( s.length() == 1 ) s = "0"+s;
            System.out.print( " " + s );
        }
        System.out.println( "" );
    }
    
    
    public static void main( String[] args ) throws IOException
    {
        if( args.length != 3 )
        {
            String s = "Usage: TCPEchoServer port serverId serverName";
            System.out.println( s );
            return;
        }
        
        int port    = Integer.parseInt( args[0] );
        int id      = Integer.parseInt( args[1] );
        String name = args[2];
        
        TCPEchoServer server = new TCPEchoServer(port, id, name);
        server.listenRequests();
    }
    

}
